package com.kz.framework;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.kz.framework.entity.Handler;
import com.kz.framework.entity.Param;
import com.kz.framework.entity.Request;
import com.kz.framework.entity.View;
import com.kz.framework.helper.BeanHelper;
import com.kz.framework.helper.ClassHelper;
import com.kz.framework.helper.ConfigHelper;
import com.kz.framework.helper.ControllerHelper;
import com.kz.framework.utils.ClassUtil;
import com.kz.framework.utils.ReflectionUtil;

/**
 * @author zhgp
 * @version 1.0
 * @description: 请求入口 一个servlet
 * @date 2021/6/21 21:18
 */

@WebServlet(urlPatterns = "/*", loadOnStartup = 0) // loadOnStartup 优先级
public class DispatcherServlet extends HttpServlet {

    private static final Logger LOGGER = LoggerFactory.getLogger(DispatcherServlet.class);

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        try {

            Class<?>[] classList = {ClassHelper.class, BeanHelper.class, ControllerHelper.class};
            // 加载几个核心类，执行static块
            for (Class<?> cls : classList) {
                ClassUtil.loadClass(cls.getName());
            }
            // servlet上下文
            ServletContext servletContext = servletConfig.getServletContext();
            // 注册处理jsp和静态资源
            registerServlet(servletContext);

        } catch (Exception e) {
            throw new ServletException("DispatcherServlet 初始化失败............" + e.getMessage());
        }
    }

    private void registerServlet(ServletContext servletContext) {
        // 动态注册处理JSP的Servlet
        ServletRegistration jspServlet = servletContext.getServletRegistration("jsp");
        jspServlet.addMapping(ConfigHelper.getAppJspPath() + "*");

        // 动态注册处理静态资源的默认Servlet
        ServletRegistration defaultServlet = servletContext.getServletRegistration("default");
        defaultServlet.addMapping("/favicon.ico"); // 网站头像
        defaultServlet.addMapping(ConfigHelper.getAppAssetPath() + "*");// 静态资源路径
    }

    /**
     * @description: 重写service方法
     * @param request
     * @param response
     * @return: void
     * @author zhgp
     * @date: 2021/6/22 19:02
     */
    @Override
    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String rqMethod = request.getMethod().toUpperCase();// 请求方法
        String rqPath = request.getPathInfo();// 请求path
        LOGGER.info("request path：........... " + rqPath + "  method: " + rqMethod);

        /**
         * 获取相应的控制器
         */
        Handler handler = ControllerHelper.getHandler(new Request(rqMethod, rqPath));
        if (handler == null) {
            LOGGER.error("unable to find handler.");
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        /**
         * 获得相应Controller类对象和 其 method
         */
        Class<?> controllerClass = handler.getControllerClass();
        Method actionMethod = handler.getControllerMethod();

        /**
         * 获得相应Controller bean 实例
         */
        Object controllerBean = BeanHelper.getBean(controllerClass);

        /**
         * 由HttpRequest 获得请求参数
         */
        Param param = createParam(request);

        LOGGER.info("执行控制器： " + controllerClass.getName() + " /方法：" + actionMethod.getName());

        // 调用与请求对应的方法
        Object result;
        if (param == null || param.isEmpty()) {
            result = ReflectionUtil.invokeMethod(controllerBean, actionMethod);
        } else {
            result = ReflectionUtil.invokeMethod(controllerBean, actionMethod, param);
        }

        // 跳转页面或返回json数据(视图解析)
        if (result instanceof View) {
            handleViewResult((View)result, request, response);
        } else {
            handleDataResult(result, response);
        }
    }

    /**
     * @description: 封装请求参数
     * @param request
     * @return: com.kz.framework.entity.Param
     * @author zhgp
     * @date: 2021/6/22 20:54
     */
    public static Param createParam(HttpServletRequest request) {
        Map<String, Object> paramMap = new HashMap<>();
        Enumeration<String> paramNames = request.getParameterNames();
        // 没有参数
        if (!paramNames.hasMoreElements()) {
            return null;
        }
        // get和post参数都能获取到
        while (paramNames.hasMoreElements()) {
            String fieldName = paramNames.nextElement();
            String fieldValue = request.getParameter(fieldName);
            paramMap.put(fieldName, fieldValue);
        }
        return new Param(paramMap);
    }

    /**
     * 跳转页面
     */
    private void handleViewResult(View view, HttpServletRequest request, HttpServletResponse response)
        throws IOException, ServletException {
        String path = view.getPath();
        if (StringUtils.isEmpty(path)) {
            return;
        }
        if (path.startsWith("/")) { // 重定向
            response.sendRedirect(request.getContextPath() + path);
        } else { // 请求转发
            Map<String, Object> model = view.getModel();
            for (Map.Entry<String, Object> entry : model.entrySet()) {
                request.setAttribute(entry.getKey(), entry.getValue());
            }
            request.getRequestDispatcher(ConfigHelper.getAppJspPath() + path).forward(request, response);
        }
    }

    /**
     * 返回JSON数据
     */
    private void handleDataResult(Object data, HttpServletResponse response) throws IOException {
        if (data != null) {
            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8");
            PrintWriter writer = response.getWriter();
            String json = JSON.toJSON(data).toString();
            writer.write(json);
            writer.flush();
            writer.close();
        }
    }
}
